package org.nekperu15739.me.persistencejpa.dao.abstracts;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nekperu15739.me.persistencejpa.dao.interfaces.Dao;
import org.nekperu15739.me.persistencejpa.entities.abstracts.EntityAbstract;

public abstract class DaoAbstract<E extends EntityAbstract<P>, P extends Serializable> implements
        Dao<E, P> {

    private final Log logger = LogFactory.getLog(getClass());

    private EntityManager entityManager;

    private Class<E> entity;

    @SuppressWarnings("unchecked")
    public DaoAbstract() {
        Type genericSuperclass = getClass().getSuperclass()
                .getGenericSuperclass();
        getLogger().info(genericSuperclass);

        if (genericSuperclass instanceof ParameterizedType) {
            ParameterizedType genericSuperclassCasted = (ParameterizedType) genericSuperclass;
            Type[] typeList = genericSuperclassCasted.getActualTypeArguments();
            Type type = typeList[0];
            Class<E> classType = ((Class<E>) type);
            getLogger().info(classType);
            setEntity(classType);
        }
    }

    @SuppressWarnings("unchecked")
    public E select(E entity) {
        P primaryKey = entity.getPrimaryKey();
        E entityOut = (E) getEntityManager()
                .find(entity.getClass(), primaryKey);
        return entityOut;
    }

    public void insert(E entity) {
        getLogger().info(entity);
        getEntityManager().persist(entity);
        getLogger().info(entity);
    }

    public E update(E entity) {
        return (E) getEntityManager().merge(entity);
    }

    /**
     * Borrar entidades, en caso de no estar en cache seleccionamos el registro
     * 
     * @param E entity
     */
    public void delete(E entity) {
        E entityOut;
        if (!getEntityManager().contains(entity)) {
            entityOut = select(entity);
        } else {
            entityOut = entity;
        }
        getEntityManager().remove(entityOut);
    }

    public void flush() {
        getEntityManager().flush();
        getLogger().info("flush");
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    /**
     * String jpaqlString = "select e from :table e".replace("table",
     * getEntity().getSimpleName().toString()); TypedQuery<E> query =
     * getEntityManager().createQuery(jpaqlString, getEntity()); return
     * query.getResultList();
     */
    @Override
    public List<E> selectList() {
        CriteriaBuilder criteriaBuilder = getEntityManager()
                .getCriteriaBuilder();
        CriteriaQuery<E> query = criteriaBuilder.createQuery(getEntity());
        Root<E> from = query.from(getEntity());
        CriteriaQuery<E> criteriaQuery = query.select(from);
        TypedQuery<E> typedQuery = getEntityManager()
                .createQuery(criteriaQuery);

        return typedQuery.getResultList();
    }

    protected void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    protected EntityManager getEntityManager() {
        return entityManager;
    }

    private Log getLogger() {
        return logger;
    }

    private Class<E> getEntity() {
        return entity;
    }

    private void setEntity(Class<E> entity) {
        this.entity = entity;
    }

}